import CryptoJS from 'crypto-js'
import SMCryptoJS from 'miniprogram-sm-crypto'
import cacheHandler from '@/utils/cacheHandler.js'

function encryptSMSign(data) {
  var signJson = {}
  for (var key in data) {
    if (key !== 'biz_content') {
      signJson[key] = data[key]
    }
  }
  var bizContent = data.biz_content
  if (typeof bizContent !== 'object') {
    bizContent = JSON.parse(bizContent)
  }
  for (var bizKey in bizContent) {
    if (typeof bizContent[bizKey] === 'object') {
      var jsonString = JSON.stringify(bizContent[bizKey])
      jsonString = jsonString.replace(/\//g, '\\/')
      signJson[bizKey] = jsonString
    } else {
      signJson[bizKey] = bizContent[bizKey]
    }
  }
  var keyArray = []
  for (var signKey in signJson) {
    keyArray.push(signKey)
  }
  keyArray = keyArray.sort()
  var md5String = ''
  keyArray.forEach(key => {
    md5String = md5String + key + '=' + signJson[key] + '&'
  })
  md5String = md5String.substr(0, md5String.length - 1)
  md5String = md5String + cacheHandler.getCrypto().cryptoSignKey
  let hashData = SMCryptoJS.sm3(md5String)
  return hashData.toString().toUpperCase()
}

function encryptSM(word) {
  let encrypted = ''
  let key = strToHexCharCode(strToUtf8Bytes(cacheHandler.getCrypto().cryptoKey))
  let iv = strToHexCharCode(strToUtf8Bytes(cacheHandler.getCrypto().cryptoIV))
  if (typeof word === 'string') {
    encrypted = SMCryptoJS.sm4.encrypt(word, key, {
      mode: 'cbc',
      iv: iv
    })
  } else if (typeof word === 'object') {
    word = JSON.stringify(word)
    encrypted = SMCryptoJS.sm4.encrypt(word, key, {
      mode: 'cbc',
      iv: iv
    })
  }
  return encrypted.toString().toUpperCase()
}

function decryptSM(word) {
  let key = strToHexCharCode(strToUtf8Bytes(cacheHandler.getCrypto().cryptoKey))
  let iv = strToHexCharCode(strToUtf8Bytes(cacheHandler.getCrypto().cryptoIV))
  let decrypt = SMCryptoJS.sm4.decrypt(word, key, {
    mode: 'cbc',
    iv: iv
  })
  return decrypt
}

function encryptMD5Sign(data) {
  var signJson = {}
  for (var key in data) {
    if (key !== 'biz_content') {
      signJson[key] = data[key]
    }
  }

  var bizContent = data.biz_content
  if (typeof bizContent !== 'object') {
    bizContent = JSON.parse(bizContent)
  }
  for (var bizKey in bizContent) {
    if (typeof bizContent[bizKey] === 'object') {
      var jsonString = JSON.stringify(bizContent[bizKey])
      jsonString = jsonString.replace(/\//g, '\\/')
      signJson[bizKey] = jsonString
    } else {
      signJson[bizKey] = bizContent[bizKey]
    }
  }
  var keyArray = []
  for (var signKey in signJson) {
    keyArray.push(signKey)
  }
  keyArray = keyArray.sort()
  var md5String = ''
  keyArray.forEach(key => {
    md5String = md5String + key + '=' + signJson[key] + '&'
  })
  md5String = md5String.substr(0, md5String.length - 1)
  md5String = md5String + cacheHandler.getCrypto().cryptoSignKey
  return CryptoJS.MD5(md5String).toString().toUpperCase()
}

function encryptAES(word) {
  let encrypted = ''
  let key = CryptoJS.enc.Utf8.parse(cacheHandler.getCrypto().cryptoKey)
  let iv = CryptoJS.enc.Utf8.parse(cacheHandler.getCrypto().cryptoIV)
  if (typeof word === 'string') {
    const srcs = CryptoJS.enc.Utf8.parse(word)
    encrypted = CryptoJS.AES.encrypt(srcs, key, {
      iv: iv,
      mode: CryptoJS.mode.CBC,
      padding: CryptoJS.pad.Pkcs7
    })
  } else if (typeof word === 'object') {
    var data = JSON.stringify(word)
    const srcs = CryptoJS.enc.Utf8.parse(data)
    encrypted = CryptoJS.AES.encrypt(srcs, key, {
      iv: iv,
      mode: CryptoJS.mode.CBC,
      padding: CryptoJS.pad.Pkcs7
    })
  }
  return encrypted.ciphertext.toString()
}

function decryptAES(word) {
  var key = CryptoJS.enc.Utf8.parse(cacheHandler.getCrypto().cryptoKey)
  var iv = CryptoJS.enc.Utf8.parse(cacheHandler.getCrypto().cryptoIV)
  const encryptedHexStr = CryptoJS.enc.Hex.parse(word)
  const srcs = CryptoJS.enc.Base64.stringify(encryptedHexStr)
  const decrypt = CryptoJS.AES.decrypt(srcs, key, {
    iv: iv,
    mode: CryptoJS.mode.CBC,
    padding: CryptoJS.pad.Pkcs7
  })
  const decryptedStr = decrypt.toString(CryptoJS.enc.Utf8)
  return decryptedStr.toString()
}

function encryptSign(data) {
  if (cacheHandler.getCrypto().cryptoSignType === 'SM') {
    return encryptSMSign(data)
  } else {
    return encryptMD5Sign(data)
  }
}

function encrypt(word) {
  if (cacheHandler.getCrypto().cryptoType === 'SM') {
    return encryptSM(word)
  } else {
    return encryptAES(word)
  }
}

function decrypt(word) {
  if (cacheHandler.getCrypto().cryptoType === 'SM') {
    return decryptSM(word)
  } else {
    return decryptAES(word)
  }
}

function strToHexCharCode(str) {
  var hexCharCode = []
  var chars = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F']
  for (var i = 0; i < str.length; i++) {
    var bit = (str[i] & 0x0f0) >> 4
    hexCharCode.push(chars[bit])
    var bit = str[i] & 0x0f
    hexCharCode.push(chars[bit])
  }
  return hexCharCode.join('')
}

function strToUtf8Bytes(str) {
  const utf8 = []
  for (let ii = 0; ii < str.length; ii++) {
    let charCode = str.charCodeAt(ii)
    if (charCode < 0x80) utf8.push(charCode)
    else if (charCode < 0x800) {
      utf8.push(0xc0 | (charCode >> 6), 0x80 | (charCode & 0x3f))
    } else if (charCode < 0xd800 || charCode >= 0xe000) {
      utf8.push(0xe0 | (charCode >> 12), 0x80 | ((charCode >> 6) & 0x3f), 0x80 | (charCode & 0x3f))
    } else {
      ii++
      charCode = 0x10000 + (((charCode & 0x3ff) << 10) | (str.charCodeAt(ii) & 0x3ff))
      utf8.push(
        0xf0 | (charCode >> 18),
        0x80 | ((charCode >> 12) & 0x3f),
        0x80 | ((charCode >> 6) & 0x3f),
        0x80 | (charCode & 0x3f)
      )
    }
  }
  for (let jj = 0; jj < utf8.length; jj++) {
    var code = utf8[jj]
    if (code > 127) {
      utf8[jj] = code - 256
    }
  }
  return utf8
}

export default {
  encrypt,
  decrypt,
  encryptSign
}
